package com.appspot.twit;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import net.sourceforge.stripes.action.ActionBeanContext;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.http.AccessToken;
import twitter4j.http.RequestToken;

import com.appspot.manager.PMF;
import com.appspot.model.IdAndAccessToken;

public class TwitSession {

	private static final Logger logger = Logger.getLogger(TwitSession.class
			.getName());

	private static String consumerKey = "SFwBXM6TQrWTBDHc3dz2Nw";
	private static String consumerSecret = "v8PY1EUrTCih7pCOjjeiGTR64CTJHO7w8ejcePDca08";

	private static final String PARAM_OAUTH_TOKEN = "oauth_token";
	private static final String PARAM_STATUS = "status";
	
	private static final String ATTR_REQUEST_TOKEN = "request_token";
	private static final String ATTR_ACCESS_TOKEN = "access_token";
	
	private static final String PAGE_UPDATE_STATUS = "update_twitter_status.jsp";
	
	private static final String COOKIE_TWITTER_ID = "twitter_id";
	
	private static final String SEPARATOR = "&";

	private HttpSession sessionCheck(ActionBeanContext ctx) {

		HttpSession session = ctx.getRequest().getSession(false);
		if (session == null) {
			session = ctx.getRequest().getSession(true);
			logger.info("Session is null, new session created");
		} else {
			logger.info("Session is NOT null, session gotted");
		}

		return session;
	}

	@SuppressWarnings("deprecation")
	public HttpSession getOAuthToken(ActionBeanContext ctx) throws ServletException, IOException{
		HttpSession session = sessionCheck(ctx);
		HttpServletRequest req = ctx.getRequest();
		HttpServletResponse resp = ctx.getResponse();
		
		String oauthToken = ctx.getRequest().getParameter(PARAM_OAUTH_TOKEN);
		logger.info("oauthToken : " + oauthToken);
		if (oauthToken != null) {
			logger.info("Call back from Twitter, " + PARAM_OAUTH_TOKEN
					+ " received");
			String rt = (String) session.getAttribute(ATTR_REQUEST_TOKEN);
			logger.info("ATTR_REQUEST_TOKEN retrieved from session");
			if (rt == null) {
				logger.severe("ATTR_REQUEST_TOKEN is null");
				throw new ServletException("ATTR_REQUEST_TOKEN is null");
			}

			String[] rttts = rt.split(SEPARATOR);
			Twitter twitter = new Twitter();
			twitter.setOAuthConsumer(consumerKey, consumerSecret);
			try {
				AccessToken accessToken = twitter.getOAuthAccessToken(rttts[0],
						rttts[1]);
				twitter.setOAuthAccessToken(accessToken);

				session.removeAttribute(ATTR_REQUEST_TOKEN);
				logger.info("ATTR_REQUEST_TOKEN removed from session");
				session.setAttribute(ATTR_ACCESS_TOKEN, accessToken.getToken()
						+ SEPARATOR + accessToken.getTokenSecret());
				logger.info("ATTR_ACCESS_TOKEN saved in session");

				int id = twitter.verifyCredentials().getId();
				logger.info("Access token retrieved for user " + id
						+ " from Twitter");

				storeAccessToken("" + id, accessToken);

				// Set cookie, valid for 2 years
				Cookie cookie = new Cookie(COOKIE_TWITTER_ID, "" + id);
				cookie.setMaxAge(63072000); // Valid for 2 years
				resp.addCookie(cookie);
				logger.info("Cookie set for user " + id);

				// Get last status and friends' timelines
				//getMyLastStatusAndStoreInSession(session);
				//getFriendsTimelinesAndStoreInSession(session);

				// Go to the update status page
				logger.info("Going to the update satus page...");
				req.getRequestDispatcher(PAGE_UPDATE_STATUS).forward(req, resp);
			} catch (TwitterException e) {
				logger.severe("Failed to retrieve access token - "
						+ e.getMessage());
				throw new ServletException(e);
			}
		}
		
		return session;
	}
	
	public void signIn(ActionBeanContext ctx) throws ServletException, IOException{
		logger.info("Action: sign in");

		HttpSession session = getOAuthToken(ctx);
		HttpServletRequest req = ctx.getRequest();
		HttpServletResponse resp = ctx.getResponse();
		
		Twitter twitter = new Twitter();
		twitter.setOAuthConsumer(consumerKey, consumerSecret);
		
		System.out.println("signIn PHASE 1");

		// Try to load Twitter ID from cookies
		String id = null;
		Cookie[] cookies = req.getCookies();
		if (cookies != null) {
			Cookie cookie;
			for (int i = 0; i < cookies.length; i++) {
				cookie = cookies[i];
				if (COOKIE_TWITTER_ID.equals(cookie.getName())) {
					id = cookie.getValue();
					logger.fine("Twitter ID loaded from cookie - " + id);
				}
			}
		}

		System.out.println("signIn PHASE 2");
		// Try to load access token if user's Twitter ID is retrieved
		AccessToken accessToken = null;
		if (id != null) {
			System.out.println("signIn PHASE 2 - id NOT NULL");
			accessToken = loadAccessToken(id);
			if (accessToken != null) {
				System.out.println("signIn PHASE 2 - accessToken NOT NULL");
				logger.info("Access token loaded from datastore");
				session.setAttribute(ATTR_ACCESS_TOKEN, accessToken
						.getToken()
						+ SEPARATOR + accessToken.getTokenSecret());
				logger.info("ATTR_ACCESS_TOKEN saved in session");

				twitter.setOAuthAccessToken(accessToken);
				// Get last status and friends' timelines
				/*try {
					getMyLastStatusAndStoreInSession(session);
					getFriendsTimelinesAndStoreInSession(session, true);
				} catch (TwitterException e) {
					throw new ServletException(e);
				}*/

				// Go to the update status page
				logger.info("Going to the update satus page...");
				req.getRequestDispatcher(PAGE_UPDATE_STATUS).forward(req,
						resp);
			}
		}

		// Can not load access token, go to Twitter for authentication
		if (accessToken == null) {
			System.out.println("signIn PHASE 2 - accessToken NULL");
			try {
				RequestToken requestToken = twitter.getOAuthRequestToken();
				String authorisationUrl = requestToken
						.getAuthorizationURL();
				session.setAttribute(ATTR_REQUEST_TOKEN, requestToken
						.getToken()
						+ SEPARATOR + requestToken.getTokenSecret());
				logger.info("ATTR_REQUEST_TOKEN saved in session");
				logger.info("Redirecting user to " + authorisationUrl);
		
				resp.sendRedirect(authorisationUrl);
				
			} catch (TwitterException e) {
				logger.info("Sign in with Twitter failed - "
						+ e.getMessage());
				throw new ServletException(e);
			}
		}

	}
	
	private void storeAccessToken(String id, AccessToken accessToken) {
		if (accessToken == null) {
			throw new IllegalArgumentException(
					"Access token to be saved is null");
		}

		// Notice id starts with a digit number, not allowed
		IdAndAccessToken t = new IdAndAccessToken('$' + id, accessToken
				.getToken(), accessToken.getTokenSecret(),new Date());
		
		if(t!=null){
			PersistenceManager pm = PMF.get().getPersistenceManager();
			try {
				pm.makePersistent(t);
				logger.fine("Access token saved to datastore for Twitter user "
						+ id);
			} finally{
				pm.close();
			}
		}
	}

	@SuppressWarnings("unchecked")
	private AccessToken loadAccessToken(String id) {
		if (id == null || id.trim().equals("")) {
			throw new IllegalArgumentException("ID to be queried is null");
		}
		
		PersistenceManager pm = PMF.get().getPersistenceManager();
		String query = "select from " + IdAndAccessToken.class.getName()
				+ " where id == "+id + " order by date desc";
		System.out.println(query);
		List<IdAndAccessToken> tokens = (List<IdAndAccessToken>) pm.newQuery(query).execute();
		IdAndAccessToken t = null;
		try{
			if(tokens.size() >= 1){
				t = tokens.get(0);
				
			}
		} finally{
			pm.close();
		}
		if(t!=null)
			return new AccessToken(t.getToken(), t.getTokenSecret());
		else
			return null;
	}
}
